package com.ddw.meedeeuh.shared.movie.scrapers.imdb;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.ddw.meedeeuh.shared.http.HttpException;
import com.ddw.meedeeuh.shared.http.HttpUtils;
import com.ddw.meedeeuh.shared.io.SourceFile;
import com.ddw.meedeeuh.shared.movie.MovieSettings;
import com.ddw.meedeeuh.shared.movie.scrapers.MovieFileUtils;
import com.ddw.meedeeuh.shared.movie.scrapers.SearchMovieResult;
import com.ddw.meedeeuh.shared.movie.scrapers.SearchMovieScraper;
import com.ddw.meedeeuh.shared.movie.scrapers.SearchMovieScraperResult;
import com.ddw.meedeeuh.shared.movie.scrapers.SearchMovieScraperResultFactory;
import com.ddw.meedeeuh.shared.movie.scrapers.MovieScraperResultType;

/**
 * Represents a search movie scraper which contacts http://www.imdb.com/
 * and attempts to screen scrape search results.
 * @author cmcnicholas
 *
 */
class ImdbSearchMovieScraper implements SearchMovieScraper {

	private static Logger LOGGER = Logger.getLogger(ImdbSearchMovieScraper.class.getName());
	private static String IMDB_SEARCH_URL = "http://www.imdb.com/search/title?count=250&release_date=%d&title=%s&title_type=feature&view=simple";
	private static Pattern IMDB_SEARCH_MOVIE_PATTERN = Pattern.compile("<a href=\"/title/([^/\"]+)/\">([^<]+)</a>[^<]+<span[^>]+>\\(([0-9]+)\\)");

	@Override
	public SearchMovieScraperResult scrape(MovieSettings settings, SourceFile file) {
		LOGGER.finer("Entering Imdb Search Movie Scraper");

		// Get the movie name
		LOGGER.finest("Extracting movie name");
		final String movieName = MovieFileUtils.extractMovieName(file, settings);
		LOGGER.finer("Extracted movie name: " + (movieName == null ? "NULL" : movieName));

		// Get the movie year
		LOGGER.finest("Extracting movie year");
		final Integer movieYear = MovieFileUtils.extractMovieYear(file, settings);
		LOGGER.finer("Extracted movie year: " + (movieYear == null ? "NULL" : movieYear.toString()));

		// Check scraped name
		if(movieName == null || movieName.isEmpty()) {
			LOGGER.finer("Movie name was null or empty, exiting Imdb Movie Scraper");
			return SearchMovieScraperResultFactory.create(MovieScraperResultType.FAILED_PARSE);
		}

		// Check scraped year
		if(movieYear == null || movieYear < 1900) {
			LOGGER.finer("Movie year was null or invalid, exiting Imdb Movie Scraper");
			return SearchMovieScraperResultFactory.create(MovieScraperResultType.FAILED_PARSE);
		}

		// Create get the search page results
		LOGGER.finest("Retrieving search pages");
		final String page;
		try {
			page = getSearchPage(movieName, movieYear);
		} catch (Exception e) {
			LOGGER.log(Level.SEVERE, "Failed to retrieve search page", e);
			return SearchMovieScraperResultFactory.create(MovieScraperResultType.FAILED_NETWORK);
		}
		LOGGER.finest("Finished retrieving search page");

		// Parse the responses into matched movies
		LOGGER.finest("Parsing search pages");
		final List<SearchMovieResult> movies;
		try {
			movies = parseMovies(file, page);
		} catch (Exception e) {
			LOGGER.log(Level.SEVERE, "Failed to parse search pages", e);
			return SearchMovieScraperResultFactory.create(MovieScraperResultType.FAILED_PARSE);
		}
		LOGGER.finest("Finished parsing search pages, " + movies.size() + " movies found");

		LOGGER.finer("Exiting Imdb Search Movie Scraper");
		return SearchMovieScraperResultFactory.create(MovieScraperResultType.SUCCESS, movies);
	}

	private List<SearchMovieResult> parseMovies(SourceFile file, String page) {
		final List<SearchMovieResult> movies = new ArrayList<SearchMovieResult>();
		final Matcher movieMatcher = IMDB_SEARCH_MOVIE_PATTERN.matcher(page);
		while(movieMatcher.find()) {
			LOGGER.finest("Movie found, parsing values");
			try {
				final String imdbId = movieMatcher.group(1);
				final String name = movieMatcher.group(2);
				final int year = Integer.parseInt(movieMatcher.group(3));
				movies.add(ImdbSearchMovieResultFactory.create(file, imdbId, name, year));
			} catch (NumberFormatException e) {
				LOGGER.log(Level.WARNING, "Failed to parse movie year", e);
			} catch (Exception e) {
				LOGGER.log(Level.SEVERE, "Failed to parse movie values", e);
			}
		}
		return movies;
	}

	private String getSearchPage(String movieName, int movieYear) throws HttpException {
		LOGGER.finest("Creating imdb url");
		final String url = String.format(IMDB_SEARCH_URL, movieYear, HttpUtils.encode(movieName));
		LOGGER.finest("Requesting page at " + url);
		final String page = HttpUtils.get(url);
		LOGGER.finer("Request returned " + page.length() + " characters");
		return page;
	}

}
